use derive_builder::Builder;
use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::{
    error::Error,
    modeling::{
        Class, ComparsionOperator, LogicalOperator, PagingResponse, RelationDirection,
        RelationRelatedType, RelationType, RelationTypeOperator, Value, ValueOperator,
    },
};

use crate::database::DataSource;

use super::{AttributeCondition, QueryCondition};

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "ClassRelationConditionBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct ClassRelationCondition {
    pub conditions: RelationCondition,
    pub attributes: Option<Vec<AttributeCondition>>,
    pub class_conditions: Option<QueryCondition>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "RelationConditionBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct RelationCondition {
    pub class_identifier: Option<String>,
    pub relation_identifier: Option<String>,
    pub direction: Option<RelationDirection>,
    pub related_type: Option<RelationRelatedType>,
    pub related_at: Option<String>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct RelationTypeCondition {
    pub operator: RelationTypeOperator,
    pub relation_type: RelationType,
}

impl ClassRelationCondition {
    pub fn with_class_codition(
        condition: RelationCondition,
        class_conditions: QueryCondition,
    ) -> anyhow::Result<ClassRelationCondition> {
        let res = Self {
            conditions: condition,
            attributes: None,
            class_conditions: Some(class_conditions),
        };
        Ok(res)
    }
}

impl RelationCondition {
    pub fn with_class_identifier(
        direction: RelationDirection,
        class_identifier: String,
    ) -> anyhow::Result<RelationCondition> {
        let res = Self {
            class_identifier: Some(class_identifier),
            relation_identifier: None,
            direction: Some(direction),
            related_at: None,
            related_type: None,
        };
        Ok(res)
    }
}

impl RelationCondition {
    pub fn combine(&mut self, condition: &RelationCondition) -> anyhow::Result<()> {
        //class_identifier
        if let Some(items) = &condition.class_identifier {
            self.class_identifier = Some(items.clone());
        }
        //relation_identifier
        if let Some(items) = &condition.relation_identifier {
            self.relation_identifier = Some(items.clone());
        }
        //related_at
        if let Some(items) = &condition.related_at {
            self.related_at = Some(items.clone());
        }
        //direction
        if let Some(items) = &condition.direction {
            self.direction = Some(items.clone());
        }
        //related_type
        if let Some(items) = &condition.related_type {
            self.related_type = Some(items.clone());
        }
        Ok(())
    }

    pub fn merge(&mut self, conditions: &Vec<RelationCondition>) -> anyhow::Result<()> {
        //
        for condition in conditions {
            self.combine(condition)?;
        }
        Ok(())
    }
}
